package com.example.ortools.start.cp;

import com.google.ortools.Loader;
import com.google.ortools.linearsolver.MPConstraint;
import com.google.ortools.linearsolver.MPObjective;
import com.google.ortools.linearsolver.MPSolver;
import com.google.ortools.linearsolver.MPVariable;
import com.google.ortools.sat.*;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author: peng.li
 * @date: 2024/05/31
 * @description:
 */
public class HelperOpt {
    static class Cargo {
        public String spec;
        public int stock;
        public int boxSize;

        public Cargo(String spec, int stock, int boxSize) {
            this.spec = spec;
            this.stock = stock;
            this.boxSize = boxSize;
        }

        public String getSpec() {
            return spec;
        }

        public void setSpec(String spec) {
            this.spec = spec;
        }

        public int getStock() {
            return stock;
        }

        public void setStock(int stock) {
            this.stock = stock;
        }

        public int getBoxSize() {
            return boxSize;
        }

        public void setBoxSize(int boxSize) {
            this.boxSize = boxSize;
        }
    }

    static class GoodsSpecReleate{
        String goodsId;
        List<Cargo> cargos;

        public GoodsSpecReleate(String goodsId, List<Cargo> cargos) {
            this.goodsId = goodsId;
            this.cargos = cargos;
        }
    }

    public void slove(List<GoodsSpecReleate> releates){

        CpModel model = new CpModel();
        int lowerBound = 0; // 初始化为最大整数
        int upperBound = Integer.MIN_VALUE; // 初始化为最小整数

        for(GoodsSpecReleate releate : releates){
            //获取releate.cargos里面stock属性的相加值
            upperBound = Math.max(upperBound, releate.cargos.stream().mapToInt(Cargo::getStock).sum());
        }
        //定义辅助变量
        IntVar helperVar = model.newIntVar(lowerBound, upperBound, "helperVar");
        List<List<IntVar>> allList = new ArrayList<>();
        for(int i = 0; i < releates.size(); i++){
            String tempGoodsId = releates.get(i).goodsId;
            List<Cargo> tempCargos = releates.get(i).cargos;
            List<IntVar> tempVarList = new ArrayList<>();

            for(int j = 0; j < tempCargos.size(); j++){
                String tempSpec = tempCargos.get(j).spec;
                int tempStock = tempCargos.get(j).stock;
                int tempBoxSize = tempCargos.get(j).boxSize;
                int tempUpVal = tempStock / tempBoxSize;
                IntVar tempVar = model.newIntVar(0, tempUpVal, tempGoodsId+tempSpec+j);
                tempVarList.add(tempVar);//将所有变量添加到列表中
            }
            allList.add(tempVarList);
            List<Integer> tempBoxSizeList = tempCargos.stream().map(Cargo::getBoxSize).collect(Collectors.toList());
            long[] tempBoxSizeArray = tempBoxSizeList.stream().mapToLong(Long::valueOf).toArray();
            //借助辅助变量添加限制约束，使得每个货物出库数目相同
            model.addEquality(LinearExpr.weightedSum(tempVarList.toArray(new IntVar[tempVarList.size()]), tempBoxSizeArray),helperVar);
        }

        model.maximize(helperVar);

        CpSolver solver = new CpSolver();
        CpSolverStatus status = solver.solve(model);

        if (status == CpSolverStatus.OPTIMAL || status == CpSolverStatus.FEASIBLE) {
            System.out.printf("Maximum of objective function: %f%n", solver.objectiveValue());
        } else {
            System.out.println("No solution found.");
        }

        // Statistics.
        System.out.println("Statistics");
        System.out.printf("  conflicts: %d%n", solver.numConflicts());
        System.out.printf("  branches : %d%n", solver.numBranches());
        System.out.printf("  wall time: %f s%n", solver.wallTime());

    }

    public void solve1(List<GoodsSpecReleate> releates){
//        StopWatch createWatch = new StopWatch();
//        createWatch.start();
        MPSolver solver = MPSolver.createSolver("SCIP");
//        createWatch.stop();
//        System.out.println("createWatch:"+createWatch.getTime());
        int lowerBound = 0; // 初始化为最大整数
        int upperBound = Integer.MIN_VALUE; // 初始化为最小整数

        for(GoodsSpecReleate releate : releates){
            //获取releate.cargos里面stock属性的相加值
            upperBound = Math.max(upperBound, releate.cargos.stream().mapToInt(Cargo::getStock).sum());
        }
        List<List<MPVariable>> allList = new ArrayList<>();
        MPVariable helperVar = solver.makeIntVar(lowerBound, upperBound, "helperVar");
        for(int i = 0; i < releates.size(); i++){
            String tempGoodsId = releates.get(i).goodsId;
            List<Cargo> tempCargos = releates.get(i).cargos;
            List<MPVariable> tempVarList = new ArrayList<>();

            for(int j = 0; j < tempCargos.size(); j++){
                String tempSpec = tempCargos.get(j).spec;
                int tempStock = tempCargos.get(j).stock;
                int tempBoxSize = tempCargos.get(j).boxSize;
                int tempUpVal = tempStock / tempBoxSize;
                MPVariable tempVar = solver.makeIntVar(0, tempUpVal, tempGoodsId+tempSpec+j);
                tempVarList.add(tempVar);//将所有变量添加到列表中
            }
            allList.add(tempVarList);
            MPConstraint c0 = solver.makeConstraint(0, 0, "temp"+i);
            for(int j=0;j<tempVarList.size();j++){
                c0.setCoefficient(tempVarList.get(j), tempCargos.get(j).boxSize);
            }
            c0.setCoefficient(helperVar,-1);
        }

        MPObjective objective = solver.objective();
        objective.setCoefficient(helperVar, 1);
        objective.setMaximization();

//        createWatch.reset();
//        createWatch.start();
        final MPSolver.ResultStatus resultStatus = solver.solve();
//        createWatch.stop();
//        System.out.println("resultStatus:"+createWatch.getTime());

        if (resultStatus == MPSolver.ResultStatus.OPTIMAL) {
            System.out.println("Solution:");
            System.out.println("Objective value = " + objective.value());
            for(List<MPVariable> tempVarList:allList){
                for(MPVariable tempVar:tempVarList){
                    System.out.println(tempVar.name() + ": " + tempVar.solutionValue());
                }
            }
        } else {
            System.err.println("The problem does not have an optimal solution!");
        }

        System.out.println("\nAdvanced usage:");
        System.out.println("Problem solved in " + solver.wallTime() + " milliseconds");
        System.out.println("Problem solved in " + solver.iterations() + " iterations");
    }



//    public static void main(String[] args) {
//        Loader.loadNativeLibraries();
//        HelperOpt helperOpt = new HelperOpt();
//        List<Cargo> cargos1 = new ArrayList<>();
//        cargos1.add(new Cargo("A", 10, 2));
//        cargos1.add(new Cargo("B", 15, 4));
//        List<Cargo> cargos2 = new ArrayList<>();
//        cargos2.add(new Cargo("C", 5, 2));
//        cargos2.add(new Cargo("D", 33, 5));
//        GoodsSpecReleate releate1 = new GoodsSpecReleate("goodsA", cargos1);
//        GoodsSpecReleate releate2 = new GoodsSpecReleate("goodsB", cargos2);
//
//        List<GoodsSpecReleate> releates = new ArrayList<>();
//        releates.add(releate1);
//        releates.add(releate2);
//        helperOpt.solve1(releates);
//    }

    public static void main(String[] args) {
        Loader.loadNativeLibraries();
        HelperOpt helperOpt = new HelperOpt();
        List<Cargo> cargos1 = new ArrayList<>();
//        Random random = new Random();
//        for(int i = 0; i < 300; i++){
//            int randomNumber = 30 + random.nextInt(20);
//            int size = random.nextInt(5)+1;
//            Cargo cargo = new Cargo("A"+i, randomNumber, size);
//            cargos1.add(cargo);
//        }
        cargos1.add(new Cargo("A", 1234324, 241));
        cargos1.add(new Cargo("B", 44353, 124));
        cargos1.add(new Cargo("A1", 856674, 254));
        cargos1.add(new Cargo("B1", 26345, 432));
        List<Cargo> cargos2 = new ArrayList<>();
//        for(int i = 0; i < 300; i++){
//            int randomNumber = random.nextInt(10);
//            int size = random.nextInt(3)+1;
//            Cargo cargo = new Cargo("B"+i, randomNumber, size);
//            cargos2.add(cargo);
//        }
        cargos2.add(new Cargo("C", 341235, 102));
        cargos2.add(new Cargo("D", 23214, 254));
        cargos2.add(new Cargo("E", 285654, 128));
        GoodsSpecReleate releate1 = new GoodsSpecReleate("goodsA", cargos1);
        GoodsSpecReleate releate2 = new GoodsSpecReleate("goodsB", cargos2);

        List<GoodsSpecReleate> releates = new ArrayList<>();
        releates.add(releate1);
        releates.add(releate2);
        helperOpt.solve1(releates);
    }
}
